#include "socket.h"
#include "address.h"
#include "fd_manager.h"
#include "hook.h"
#include "util.h"
#include "io_manager.h"
#include "log.h"

#include <asm-generic/socket.h>
#include <bits/types/struct_iovec.h>
#include <bits/types/struct_timeval.h>
#include <cerrno>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <memory>
#include <sstream>
#include <sys/socket.h>
#include <unistd.h>


//创建TCP Socket(满足地址类型)
Socket::ptr Socket::CreateTCP(Address::ptr address)
{
    Socket::ptr sock = std::make_shared<Socket>(address->getFamily(), TCP, 0);
    return sock;
}

//创建UDP Socket(满足地址类型)
Socket::ptr Socket::CreateUDP(Address::ptr address)
{
    Socket::ptr sock = std::make_shared<Socket>(address->getFamily(), UDP, 0);
    return sock;
}

//创建IPv4的TCP Socket
Socket::ptr Socket::CreateTCPSocket()
{
    Socket::ptr sock = std::make_shared<Socket>(IPv4, TCP, 0);
    return sock;
}
//创建IPv4的UDP Socket
Socket::ptr Socket::CreateUDPSocket()
{
    Socket::ptr sock = std::make_shared<Socket>(IPv4, UDP, 0);
    return sock;
}

//创建IPv6的TCP Socket
Socket::ptr Socket::CreateTCPSocket6()
{
    Socket::ptr sock = std::make_shared<Socket>(IPv6, TCP, 0);
    return sock;
}
//创建IPv6的UDP Socket
Socket::ptr Socket::CreateUDPSocket6()
{
    Socket::ptr sock = std::make_shared<Socket>(IPv6, UDP, 0);
    return sock;
}

//创建Unix的TCP Socket
Socket::ptr Socket::CreateUnixTCPSocket()
{
    Socket::ptr sock = std::make_shared<Socket>(UNIX, TCP, 0);
    return sock;
}
//创建Unix的UDP Socket
Socket::ptr Socket::CreateUnixUDPSocket()
{
    Socket::ptr sock = std::make_shared<Socket>(UNIX, UDP, 0);
    return sock;
}

//Socket    构造函数
Socket::Socket(int family, int type, int protocol):
m_sock(-1),
m_family(family),
m_type(type),
m_protocol(protocol), 
m_isConnected(false)
{

}

//析构函数
Socket::~Socket()
{
    close();
}

//获取发送超时时间(毫秒)
int64_t Socket::getSendTimeout()
{
    FdCtx::ptr ctx = FdMgr::GetInstance()->get(this->m_sock);
    if(ctx)
    {
        return ctx->getTimeout(SO_SNDTIMEO);
    }

    return -1;
}

//设置发送超时时间(毫秒)
void Socket::setSendTimeout(int64_t v)
{
    //秒   微秒
    struct timeval tv{int(v/1000), int(v%1000*1000)};

    //SOL_SOCKET表示设置socket的发送选项
    setOption(SOL_SOCKET, SO_SNDTIMEO, tv);
}

//获取接收超时时间(毫秒)
int64_t Socket::getRecvTimeout()
{
    FdCtx::ptr ctx = FdMgr::GetInstance()->get(this->m_sock);
    if(ctx)
    {
        return ctx->getTimeout(SO_RCVTIMEO);
    }

    return -1;
}

//设置接收超时时间(毫秒)
void Socket::setRecvTimeout(int64_t v)
{
    //秒   微秒
    struct timeval tv{int(v/1000), int(v%1000*1000)};

    //SOL_SOCKET表示设置socket的发送选项
    setOption(SOL_SOCKET, SO_RCVTIMEO, tv);
}

//获取sockopt  getsockopt
bool Socket::getOption(int level, int option, void* result, socklen_t* len)
{
    int rt = getsockopt(m_sock, level, option, result, len);
    if(rt)
    {
        log_error("getsockopt sock={}, level={}, option={}, errno-{}, errstr={}",
            m_sock, level, option, errno, strerror(errno));
        return false;
    }
    return true;
}


//设置sockopt  setsockopt
bool Socket::setOption(int level, int option, const void* result, socklen_t len)
{
    int rt = setsockopt(m_sock, level, option, result, len);
    if(rt)
    {
        log_error("setsockopt sock={}, level={}, option={}, errno-{}, errstr={}",
            m_sock, level, option, errno, strerror(errno));
        return false;
    }

    return true;
}


//接收connect链接
Socket::ptr Socket::accept()
{
    Socket::ptr sock(new Socket(m_family, m_type, m_protocol));
    int newSock = ::accept(m_sock, nullptr, nullptr);

    if(newSock == -1)
    {
        log_error("accept error, m_sock:{}, errno:{}, errstr:{}", m_sock, errno, strerror(errno));
        return nullptr;
    }

    if(sock->init(newSock))
    {
        return sock;
    }

    return nullptr;
}

//初始化sock
bool Socket::init(int sock)
{
    FdCtx::ptr ctx = FdMgr::GetInstance()->get(sock);
    if(ctx && ctx->isSocket() && !ctx->isClose())
    {
        m_sock = sock;
        m_isConnected = true;  //设置已链接
        initSock();            //设置sock参数
        getLocalAddress();     //获取本地地址
        getRemoteAddress();    //获取远端地址
        return true;
    }

    return false;
}

//初始化socket 设置sock参数
void Socket::initSock()
{
    int val = 1;
    //SO_REUSEADDR一般服务器的监听socket都应该打开它, 去除time-wait等待时间导致连接失败
    setOption(SOL_SOCKET, SO_REUSEADDR, val);

    //如果是TCP
    if(m_type == SOCK_STREAM)
    {
        //TCP_NODELAY，该选项会禁用Nagle算法。Nagle算法的作用是减少小包的数量
        setOption(IPPROTO_TCP, TCP_NODELAY, val);
    }
}

//绑定地址
bool Socket::bind(const Address::ptr addr)
{
    //如果当前m_sock无效
    if(!isValid())
    {
        newSock();
        if(!isValid()) [[unlikely]]
        {
            return false;
        }
    }

    //判断协议族是否正确
    if(addr->getFamily() != m_family) [[unlikely]]
    {
        log_error("bind sock.family:{}, addr.family:{}, addr:{}",
            m_family, addr->getFamily(), addr->toString());
        return false;
    }

    if(::bind(m_sock, addr->getAddr() , addr->getAddrLen()))
    {
        log_error("bind error, errno:{}, errstr:{}", errno, strerror(errno));
        return false;
    }
    
    getLocalAddress();
    return true;
}


//连接地址
bool Socket::connect(const Address::ptr addr, uint64_t timeout_ms)
{
    //如果当前m_sock无效
    if(!isValid())
    {
        newSock();
        if(!isValid()) [[unlikely]]
        {
            return false;
        }
    }

    //判断协议族是否正确
    if(addr->getFamily() != m_family) [[unlikely]]
    {
        log_error("connect sock.family:{}, addr.family:{}, addr:{}",
            m_family, addr->getFamily(), addr->toString());
        return false;
    }

    if(timeout_ms == (uint64_t)-1) //如果没有设置超时时间
    {
        if(::connect(m_sock, addr->getAddr(), addr->getAddrLen()))
        {
            log_error("connect error, sock:{}, addr:{}, errno:{}, errstr:{}",
                m_sock, addr->toString(), errno, strerror(errno));
            close();
            return false;
        }
    }
    else
    {
        if(connect_with_timeout(m_sock, addr->getAddr(), addr->getAddrLen(), timeout_ms))
        {
            log_error("connect_with_timeout error, sock:{}, addr:{}, errno:{}, errstr:{}, timout_ms:{}",
                m_sock, addr->toString(), errno, strerror(errno), timeout_ms);
            close();
            return false;
        }
    }

    m_isConnected = true;
    getRemoteAddress();
    getLocalAddress();
    return true;
}

bool Socket::reconnect(uint64_t timeout_ms)
{
    return true;
}

//监听socket
bool Socket::listen(int backlog)
{
    if(!isValid())
    {
        log_error("listen error, m_sock = -1");
        return false;
    }

    if(::listen(m_sock, backlog))
    {
        log_error("listen error,sock{}, errno:{}, errstr:{}", m_sock, errno, strerror(errno));
        return false;
    }

    return true;
}

//关闭socket
bool Socket::close()
{
    if(!m_isConnected && m_sock == -1)
    {
        return true;
    }

    m_isConnected = false;
    if(m_sock != -1)
    {
        ::close(m_sock);
        m_sock = -1;
    }
    return false;
}


int Socket::send(const void* buffer, size_t length, int flags)
{
    if(isConnected())
    {
        return ::send(m_sock, buffer, length, flags);
    }
    return -1;
}


int Socket::send(const iovec* buffers, size_t length, int flags)
{
    if(isConnected())
    {
        struct msghdr msg;
        memset(&msg, 0, sizeof(msghdr));
        msg.msg_iov = (iovec*)buffers;
        msg.msg_iovlen = length;
        return ::sendmsg(m_sock, &msg, flags);
    }
    return -1;
}


int Socket::sendTo(const void* buffer, size_t length, const Address::ptr to, int flags)
{
    if(isConnected())
    {
        return ::sendto(m_sock, buffer, length, flags, to->getAddr(), to->getAddrLen());
    }
    return -1;
}


int Socket::sendTo(const iovec* buffers, size_t length, const Address::ptr to, int flags)
{
    if(isConnected())
    {
        msghdr msg;
        memset(&msg, 0, sizeof(msghdr));
        msg.msg_iov = (iovec*)buffers;
        msg.msg_iovlen = length;
        msg.msg_name = to->getAddr();
        msg.msg_namelen = to->getAddrLen();
        return ::sendmsg(m_sock, &msg, flags);
    }
    return -1;
}


int Socket::recv(void* buffer, size_t length, int flags)
{
    if(isConnected())
    {
        return ::recv(m_sock, buffer, length, flags);
    }
    return -1;
}


int Socket::recv(iovec* buffers, size_t length, int flags)
{
    if(isConnected())
    {
        msghdr msg;
        memset(&msg, 0, sizeof(msghdr));
        msg.msg_iov = (iovec*)buffers;
        msg.msg_iovlen = length;
        return ::recvmsg(m_sock, &msg, flags);
    }
    return -1;
}


int Socket::recvFrom(void* buffer, size_t length, Address::ptr from, int flags)
{
    if(isConnected())
    {
        socklen_t len = from->getAddrLen();
        return ::recvfrom(m_sock, buffer, length, flags, from->getAddr(), &len);
    }
    return -1;
}


int Socket::recvFrom(iovec* buffers, size_t length, Address::ptr from, int flags)
{
    if(isConnected())
    {
        msghdr msg;
        memset(&msg, 0, sizeof(msghdr));
        msg.msg_iov = (iovec*)buffers;
        msg.msg_iovlen = length;
        msg.msg_name = from->getAddr();
        msg.msg_namelen = from->getAddrLen();
        return ::recvmsg(m_sock, &msg, flags);
    }
    return -1;
}


//获取远端地址
Address::ptr Socket::getRemoteAddress()
{
    if(m_remoteAddress)
    {
        return m_remoteAddress;
    }

    Address::ptr result;
    switch (m_family) 
    {
    case AF_INET:
        result.reset(new IPv4Address());
        break;
    case AF_INET6:
        result.reset(new IPv6Address());
        break;
    case AF_UNIX:
        result.reset(new UnixAddress());
        break;
    default:
        result.reset(new UnknownAddress(m_family));
        break;
    }
    socklen_t addrlen = result->getAddrLen();
    
    //获取远端地址
    if(getpeername(m_sock, result->getAddr(), &addrlen))
    {
        log_error("getpeername error, sock:{}, errno:{}, errstr:{}",
            m_sock, errno, strerror(errno));
        return Address::ptr(new UnknownAddress(m_family));
    }

    if(m_family == AF_UNIX) //需要从新设置addr的长度
    {
        //将Address::ptr 转为 UnixAddress::ptr
        UnixAddress::ptr addr = std::dynamic_pointer_cast<UnixAddress>(result);
        addr->setAddrLen(addrlen);
    }

    m_remoteAddress = result;
    return m_remoteAddress;
}

//获取本地地址
Address::ptr Socket::getLocalAddress()
{
    if(m_localAddress)
    {
        return m_localAddress;
    }

    Address::ptr result;
    switch (m_family) 
    {
    case AF_INET:
        result.reset(new IPv4Address());
        break;
    case AF_INET6:
        result.reset(new IPv6Address());
        break;
    case AF_UNIX:
        result.reset(new UnixAddress());
        break;
    default:
        result.reset(new UnknownAddress(m_family));
        break;
    }
    socklen_t addrlen = result->getAddrLen();

    //获取本地的套接字信息
    if(getsockname(m_sock, result->getAddr(), &addrlen))
    {
        log_error("getsockname error, sock:{}, errno:{}, errstr:{}",
            m_sock, errno, strerror(errno));
        return Address::ptr(new UnknownAddress(m_family));
    }

    if(m_family == AF_UNIX) //需要从新设置addr的长度
    {
        //将Address::ptr 转为 UnixAddress::ptr
        UnixAddress::ptr addr = std::dynamic_pointer_cast<UnixAddress>(result);
        addr->setAddrLen(addrlen);
    }

    m_localAddress = result;
    return m_localAddress;
}


//是否有效(m_sock != -1)
bool Socket::isValid() const
{
    return m_sock != -1;
}

//返回Socket错误
int Socket::getError()
{
    int error = 0;
    socklen_t len = sizeof(int);
    if(!getOption(SOL_SOCKET, SO_ERROR, &error, &len))
    {
        error = errno;
    }
    return error;
}

//输出信息到流中
std::ostream& Socket::dump(std::ostream& os) const
{
    os << "[Socket sock:" << m_sock
       << " is_connected:" << m_isConnected
       << " family:" << m_family
       << " type:" << m_type
       << " protocol:" << m_protocol;
    
    if(m_localAddress)
    {
        os << " local_address:" << m_localAddress->toString();
    }
    if(m_remoteAddress)
    {
        os << " remote_address:" << m_remoteAddress->toString();
    }
    os << "]";
    return os;
}

std::string Socket::toString() const
{
    std::stringstream oss;
    dump(oss);
    return oss.str();
}

//取消读
bool Socket::cancelRead()
{
    return IOManager::GetThis()->cancelEvent(m_sock, IOManager::READ);
}

//取消写
bool Socket::cancelWrite()
{
    return IOManager::GetThis()->cancelEvent(m_sock, IOManager::WRITE);
}

//取消accept
bool Socket::cancelAccept()
{
    return IOManager::GetThis()->cancelEvent(m_sock, IOManager::READ);
}

//取消所有事件
bool Socket::cancelAll()
{
    return IOManager::GetThis()->cancelAll(m_sock);
}

//创建socket
void Socket::newSock()
{
    m_sock = socket(m_family, m_type, m_protocol);
    if(m_sock != -1) [[likely]]
    {
        initSock();
    }
    else
    {
        log_error("socket({},{},{}) errno:{}, errstr={}",
            m_family, m_type, m_protocol, errno, strerror(errno));
    }
}

//==========================================================================

//流式输出socket
std::ostream& operator<<(std::ostream& os, const Socket& sock)
{
    return sock.dump(os);
}